
using System;
using System.Data;
using System.Collections.Generic;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using ScrewTurn.Wiki.PluginFramework;

namespace ScrewTurn.Wiki {

	/// <summary>
	/// Provides methods for moving data from a Provider to another.
	/// </summary>
	public static class DataMover {

		/// <summary>
		/// Moves <b>all</b> the data from a Pages Provider to another one.
		/// </summary>
		/// <param name="source">The source Provider.</param>
		/// <param name="destination">The destination Provider.</param>
		public static void MovePages(IPagesStorageProvider source, IPagesStorageProvider destination) {
			// Move Snippets
			Snippet[] snippets = source.AllSnippets;
			for(int i = 0; i < snippets.Length; i++) {
				destination.AddSnippet(snippets[i].Name, snippets[i].Content);
				source.RemoveSnippet(snippets[i].Name);
			}

			// Load all nav paths now to avoid problems with missing pages from source provider
			// after the pages have been moved already
			NavigationPath[] sourceNavPaths = source.AllNavigationPaths;

			// Copy categories (removed from source later)
			CategoryInfo[] sourceCats = source.AllCategories;
			for(int i = 0; i < sourceCats.Length; i++) {
				destination.AddCategory(sourceCats[i].Name);
			}

			// Move Pages
			PageInfo[] pages = source.AllPages;
			for(int i = 0; i < pages.Length; i++) {
				
				// Create Page
				PageInfo newPage = destination.AddPage(pages[i].Name, pages[i].CreationDateTime);
				if(newPage == null) {
					Log.LogEntry("Unable to move Page " + pages[i].Name + " - Skipping", EntryType.Error, "SYSTEM");
					continue;
				}
				
				// Get content and store it, without backup
				PageContent c = source.GetContent(pages[i]);
				destination.ModifyPage(newPage, c.Title, c.User, c.LastModified, c.Comment, c.Content, false);
				
				// Set status
				destination.SetStatus(newPage, pages[i].Status);
				
				// Move all the backups
				List<int> revs = source.GetBackups(pages[i]);
				for(int k = 0; k < revs.Count; k++) {
					c = source.GetBackupContent(pages[i], revs[k]);
					destination.SetBackupContent(c, revs[k]);
				}

				// Move all messages
				Message[] messages = source.GetMessages(pages[i]);
				MoveMessages(destination, newPage, new List<Message>(messages), -1);

				// Bind current page (find all proper categories, and use them to bind the page)
				List<string> pageCats = new List<string>();
				for(int k = 0; k < sourceCats.Length; k++) {
					for(int z = 0; z < sourceCats[k].Pages.Length; z++) {
						if(sourceCats[k].Pages[z].Equals(newPage.Name)) {
							pageCats.Add(sourceCats[k].Name);
							break;
						}
					}
				}
				destination.Rebind(newPage, pageCats.ToArray());

				// Remove Page from source
				source.RemovePage(pages[i]); // Also deletes the Messages
			}

			// Remove Categories from source
			for(int i = 0; i < sourceCats.Length; i++) {
				source.RemoveCategory(sourceCats[i]);
			}

			// Move navigation paths
			List<PageInfo> newPages = new List<PageInfo>(destination.AllPages);
			for(int i = 0; i < sourceNavPaths.Length; i++) {
				PageInfo[] tmp = new PageInfo[sourceNavPaths[i].Pages.Count];
				for(int k = 0; k < tmp.Length; k++) {
					tmp[k] = newPages.Find(delegate(PageInfo p) { return p.Name == sourceNavPaths[i].Pages[k]; });
				}
				destination.AddNavigationPath(sourceNavPaths[i].Name, tmp);
				source.RemoveNavigationPath(sourceNavPaths[i].Name);
			}
		}

		/// <summary>
		/// Recursively adds all the Messages of a Page to the specified Provider.
		/// </summary>
		/// <param name="prov">The destination Provider.</param>
		/// <param name="newPage">The Page.</param>
		/// <param name="messages">The Messages.</param>
		/// <param name="parent">The Parent Message's ID, or -1.</param>
		private static void MoveMessages(IPagesStorageProvider prov, PageInfo newPage, List<Message> messages, int parent) {
			for(int i = 0; i < messages.Count; i++) {
				prov.AddMessage(newPage, messages[i].Username, messages[i].Subject, messages[i].DateTime, messages[i].Body, parent);
				MoveMessages(prov, newPage, messages[i].Replies, -1);
			}
		}

		/// <summary>
		/// Moves all the User Accounts from a Provider to another.
		/// </summary>
		/// <param name="source">The source Provider.</param>
		/// <param name="destination">The destination Provider.</param>
		public static void MoveUsers(IUsersStorageProvider source, IUsersStorageProvider destination) {
			UserInfo[] users = source.AllUsers;
			MovedUser[] movedUsers = new MovedUser[users.Length];

			for(int i = 0; i < users.Length; i++) {
				// Generate new Password, create MovedUser object, add new User, delete old User
				string password = Tools.GenerateRandomPassword();
				movedUsers[i] = new MovedUser(users[i].Username, users[i].Email, password);
				destination.AddUser(users[i].Username, password, users[i].Email, users[i].Active, users[i].DateTime, users[i].Admin);
				source.RemoveUser(users[i]);
			}

			string body = Tools.LoadFile(Settings.PasswordResetMessageFile).Replace("##WIKITITLE##", Settings.WikiTitle).Replace("##EMAILADDRESS##", Settings.ContactEmail);
			string subject = Settings.WikiTitle + " - Password";
			string sender = Settings.SenderEmail;

			// Send Emails
			for(int i = 0; i < movedUsers.Length; i++) {
				EmailSender.Send(movedUsers[i].Email, sender, subject,
					body.Replace("##USERNAME##", movedUsers[i].Username).Replace("##PASSWORD##", movedUsers[i].Password), false);
			}
		}

	}

	/// <summary>
	/// Contains Username, Email and Password of a moved User account.
	/// </summary>
	public class MovedUser {

		private string username, email, password;

		/// <summary>
		/// Initializes a new instance of the <b>MovedUser</b> class.
		/// </summary>
		/// <param name="username">The Username.</param>
		/// <param name="email">The Email address.</param>
		/// <param name="password">The Password.</param>
		public MovedUser(string username, string email, string password) {
			this.username = username;
			this.email = email;
			this.password = password;
		}

		/// <summary>
		/// Gets the Username.
		/// </summary>
		public string Username {
			get { return username; }
		}

		/// <summary>
		/// Gets the Email.
		/// </summary>
		public string Email {
			get { return email; }
		}

		/// <summary>
		/// Gets the Password.
		/// </summary>
		public string Password {
			get { return password; }
		}

	}

}